#include <deque>
#include <mutex>
#include <optional>
#include <condition_variable>

namespace jizhe {
    
template<class T, class Dem_queue = std::deque<T>>
class ConQueue {
private:
    typedef std::unique_lock<std::mutex> uni_lock;
    Dem_queue m_queue;
    std::mutex m_mute;
    std::condition_variable m_cv_not_full;
    std::condition_variable m_cv_not_empty;
    std::size_t m_limit;
public:
    // 默认 (size_t)-1 表示容纳无限量元素
    ConQueue() : m_limit(static_cast<std::size_t>(-1)) {}

    // 指定最大允许堆积的元素数量，超过该数量后会阻塞
    explicit ConQueue(std::size_t limit) : m_limit(limit) {}

    void push(T value) {
        uni_lock lock(m_mute);
        m_cv_not_full.wait(lock, [this](){
            return m_queue.size() < m_limit;
        });
        m_queue.push_front(value);
        m_cv_not_empty.notify_one();
    };
    T pop() {
        uni_lock lock(m_mute);
        m_cv_not_empty.wait(lock, [this]() -> bool {
            return m_queue.size() > 0;
        });
        T value = std::move(m_queue.back());
        m_queue.pop_back();
        m_cv_not_full.notify_one();
        return value;
    };
    bool try_push(T value) {
        uni_lock lock(m_mute);
        if (m_queue.size() >= m_limit) {
            return false;
        }
        m_queue.push_front(std::move(value));
        m_cv_not_empty.notify_one();
        return true;
    }
    std::optional<T> try_pop() {
        uni_lock lock(m_mute);
        if (m_queue.empty()) {
            return std::nullopt;
        }
        T value = std::move(m_queue.back());
        m_queue.pop_back();
        m_cv_not_full.notify_one();
        return value;
    }
    std::optional<T> try_pop_for(std::chrono::steady_clock::duration timeout) {
        uni_lock lock(m_mute);
        if (!m_cv_not_empty.wait_for(&lock, timeout, [this](){return !m_queue.empty();})) {
            return std::nullopt;
        }
        T value = std::move(m_queue.back());
        m_queue.pop_back();
        m_cv_not_full.notify_one();
        return value;
    }
    std::optional<T> try_pop_until(std::chrono::steady_clock::time_point timeout) {
        uni_lock lock(m_mute);
        if (!m_cv_not_empty.wait_until(&lock, timeout, [this](){return !m_queue.empty();})) {
            return std::nullopt;
        }
        T value = std::move(m_queue.back());
        m_queue.pop_back();
        m_cv_not_full.notify_one();
        return value;
    }

    size_t size() { 
        uni_lock lock(m_mute);
        return m_queue.size();
    }
    bool empty() { 
        uni_lock lock(m_mute);
        return m_queue.empty(); 
    }
};
}
